Clock crossing violation
Introduction
SpinalHDL will check that every register of your design only depends (through combinational logic paths) on registers which use the same or a synchronous clock domain.
Example
The following code:
class TopLevel extends Component {
val clkA = ClockDomain.external("clkA")
val clkB = ClockDomain.external("clkB")
val regA = clkA(Reg(UInt(8 bits))) // PlayDev.scala:834
val regB = clkB(Reg(UInt(8 bits))) // PlayDev.scala:835
val tmp = regA + regA // PlayDev.scala:838
regB := tmp
}
will throw:
CLOCK CROSSING VIOLATION from (toplevel/regA : UInt[8 bits]) to (toplevel/regB : UInt[8 bits]).
- Register declaration at
***
Source file location of the toplevel/regA definition via the stack trace
***
- through
>>> (toplevel/regA : UInt[8 bits]) at ***(PlayDev.scala:834) >>>
>>> (toplevel/tmp : UInt[8 bits]) at ***(PlayDev.scala:838) >>>
>>> (toplevel/regB : UInt[8 bits]) at ***(PlayDev.scala:835) >>>
There are multiple possible fixes, listed below:
crossClockDomain tag
The crossClockDomain
tag can be used to communicate “It’s alright, don’t panic about this specific clock crossing” to the SpinalHDL compiler.
class TopLevel extends Component {
val clkA = ClockDomain.external("clkA")
val clkB = ClockDomain.external("clkB")
val regA = clkA(Reg(UInt(8 bits)))
val regB = clkB(Reg(UInt(8 bits))).addTag(crossClockDomain)
val tmp = regA + regA
regB := tmp
}
setSyncronousWith
You can also specify that two clock domains are synchronous together by using the setSynchronousWith
method of one of the ClockDomain
objects.
class TopLevel extends Component {
val clkA = ClockDomain.external("clkA")
val clkB = ClockDomain.external("clkB")
clkB.setSyncronousWith(clkA)
val regA = clkA(Reg(UInt(8 bits)))
val regB = clkB(Reg(UInt(8 bits)))
val tmp = regA + regA
regB := tmp
}
BufferCC
When exchanging single-bit signals (such as Bool
types), or Gray-coded values, you can use BufferCC
to safely cross different ClockDomain
regions.
Warning
Do not use BufferCC
with multi-bit signals, as there is a risk of corrupted reads on the receiving side if the clocks are asynchronous.
See the Clock Domains page for more details.
class AsyncFifo extends Component {
val popToPushGray = Bits(ptrWidth bits)
val pushToPopGray = Bits(ptrWidth bits)
val pushCC = new ClockingArea(pushClock) {
val pushPtr = Counter(depth << 1)
val pushPtrGray = RegNext(toGray(pushPtr.valueNext)) init(0)
val popPtrGray = BufferCC(popToPushGray, B(0, ptrWidth bits))
val full = isFull(pushPtrGray, popPtrGray)
...
}
val popCC = new ClockingArea(popClock) {
val popPtr = Counter(depth << 1)
val popPtrGray = RegNext(toGray(popPtr.valueNext)) init(0)
val pushPtrGray = BufferCC(pushToPopGray, B(0, ptrWidth bits))
val empty = isEmpty(popPtrGray, pushPtrGray)
...
}
}